package switchviz.core.scenario;

import java.awt.BorderLayout;
import java.awt.Frame;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.ComponentEvent;
import java.awt.event.ComponentListener;
import java.util.ArrayList;
import java.util.Iterator;

import javax.swing.BorderFactory;
import javax.swing.Box;
import javax.swing.BoxLayout;
import javax.swing.JButton;
import javax.swing.JDialog;
import javax.swing.JPanel;
import javax.swing.SwingWorker;

import org.apache.log4j.Logger;

import switchviz.core.ResourceManager;
import switchviz.core.SwitchVizException;
import switchviz.core.signal.ISignal;
import switchviz.core.view.View;
import switchviz.core.view.ViewDataModel;
import switchviz.core.view.ViewLegend;
import switchviz.gui.component.PaddingPanel;
import switchviz.gui.component.StatusNotificationLabel;
import switchviz.network.NetworkException;
import switchviz.network.NetworkHandShake;
import switchviz.network.netswitch.Switch;
import switchviz.network.netswitch.SwitchDataModel;
import switchviz.network.netswitch.SwitchScenarioConfiguration;
import switchviz.recording.Record;
import switchviz.recording.RecordDataModel;
import switchviz.recording.RecordingException;
import switchviz.recording.Replay;

public class ScenarioLoader extends JDialog implements ComponentListener, ActionListener
{
	/**
	 * 
	 */
	private static final long serialVersionUID = 3280113425121566394L;
	private static Logger logger  = Logger.getLogger(ScenarioLoader.class);
	private SwitchScenarioConfiguration _switchconf = null;
	private Record _record = null;
	private View _view = null;
	private Switch _switch = null;
	private RecordDataModel _recorddm = null;
	private Replay _replay = null;
	private int _connectionstate = -1;
	private StatusNotificationLabel _snview = null;
	private StatusNotificationLabel _snconnection = null;
	private StatusNotificationLabel _snswitch = null;
	private StatusNotificationLabel _snrecord = null;
	private StatusNotificationLabel _snreplay = null;
	private JButton _buttstart = null;
	private JButton _buttcancel = null;
	private SwitchDataModel _switchdm = null;
	private ViewDataModel _viewdm = null;
	private ScenarioDataModel _scenedm = null;
	private NetworkHandShake _nhs = null;
	
	public ScenarioLoader(Frame owner, SwitchDataModel switchdm, ViewDataModel viewdm, ScenarioDataModel scenedm, SwitchScenarioConfiguration switchconf, RecordDataModel recorddm)
	{
		super(owner, true);
		_switchconf = switchconf;
		_switchdm = switchdm;
		_viewdm = viewdm;
		_scenedm = scenedm;
		_recorddm = recorddm;
		
		initialize();
		
		if(logger.isInfoEnabled())
			logger.info("Scenario Loader started...");
		
//		if(_viewdm == null)
//			_buttstart.setText("Start Recording");
		
		this.setSize(600, 300);
		this.setLocationRelativeTo(null);
		this.setTitle("Load Scenario");
		this.addComponentListener(this);
		this.setVisible(true);
	}
	
	public ScenarioLoader(Frame owner, SwitchDataModel switchdm, ViewDataModel viewdm, ScenarioDataModel scenedm)
	{
		this(owner, switchdm, viewdm, scenedm, null, null);
	}
	
	public ScenarioLoader(Frame owner, SwitchDataModel switchdm, ScenarioDataModel scenedm, SwitchScenarioConfiguration switchconf)
	{
		this(owner, switchdm, null, scenedm, switchconf, null);
	}
	
	public ScenarioLoader(Frame owner, SwitchDataModel switchdm, ViewDataModel viewdm, ScenarioDataModel scenedm, SwitchScenarioConfiguration switchPswichconfanel) {
		this(owner, switchdm, viewdm, scenedm, switchPswichconfanel, null);
	}

	public ScenarioLoader(Frame owner, ViewDataModel viewdm,
			ScenarioDataModel scenedm, RecordDataModel record) {
		this(owner, null, viewdm, scenedm, null, record);
	}

	private void initialize()
	{
		this.setLayout(ResourceManager.getInstance().getBorderLayout());
		PaddingPanel centerPanel = new PaddingPanel();
		centerPanel.setLayout(new BoxLayout(centerPanel, BoxLayout.PAGE_AXIS));
		centerPanel.setBorder(BorderFactory.createLoweredBevelBorder());
		_snconnection = new StatusNotificationLabel("");
		_snrecord = new StatusNotificationLabel("");
		_snview = new StatusNotificationLabel("");
		_snswitch = new StatusNotificationLabel("");
		_snreplay = new StatusNotificationLabel("");
		
		_snconnection.setVisible(false);
		_snrecord.setVisible(false);
		_snview.setVisible(false);
		_snswitch.setVisible(false);
		_snreplay.setVisible(false);
		
		if(_switchdm != null)
		{
			_snconnection.setMessage("Create and Checking Connection to Switch " + _switchdm.getName() + " @ " + _switchdm.getAddress() + ":" + _switchdm.getPort() + " ...");
			centerPanel.add(_snconnection);
		}
		if(_switchconf != null)
		{
			_snrecord.setMessage("Creating Record Files in " + _switchconf.getRecordFile().getAbsolutePath());
			centerPanel.add(_snrecord);
		}
		if(_viewdm != null && (_switchdm != null || _recorddm != null))
		{
			_snview.setMessage("Initialize View " + _viewdm.getName() + " ...");
			centerPanel.add(_snview);
		}
		if(_switchdm != null)
		{
			_snswitch.setMessage("Adding Signals to Switch " + _switchdm.getName() + " ...");
			centerPanel.add(_snswitch);
		}
		
		if(_recorddm != null)
		{
			_snreplay.setMessage("Open replay files in "  + " ...");
			centerPanel.add(_snreplay);
		}
		
		_buttstart = new JButton("Open Scenario");
		_buttstart.setActionCommand(ResourceManager.getInstance().getActionNew());
		_buttstart.addActionListener(this);
		_buttstart.setEnabled(false);
		_buttcancel = new JButton("Cancel");
		_buttcancel.setActionCommand(ResourceManager.getInstance().getActionCancel());
		_buttcancel.addActionListener(this);
		
        JPanel buttonpanel = new JPanel();
        buttonpanel.setLayout(new BoxLayout(buttonpanel, BoxLayout.LINE_AXIS));
        buttonpanel.add(Box.createHorizontalGlue());
        buttonpanel.add(_buttstart);
        buttonpanel.add(_buttcancel);
		
		this.add(centerPanel, BorderLayout.CENTER);
		this.add(buttonpanel, BorderLayout.SOUTH);
	}
	
	private Record loadRecord() throws RecordingException, NetworkException
	{
		_snrecord.setVisible(true);
		if(_switchconf.getRecordFile() == null || _switchconf.getAliasList() == null) return null;
		Record record = new Record(_switchconf.getRecordFile());
		record.setSwitchDataModel(_switchdm);
		for(int i = 0; i < _switchconf.getAliasList().length; ++i)
			record.add(_switchconf.getAliasList()[i]);
		return record;
	}
	
	private void checkRecord()
	{
		if(_record != null)
		{
			String message = _record.getSignals().size() + " Record Signals have been successfully created ";
			if(logger.isInfoEnabled())
				logger.info(message);
			_snrecord.setNewState(StatusNotificationLabel.SUCCESS, message);
		}
		else
		{
			String message = "Could not create Record Files in " + _switchconf.getRecordFile().getAbsolutePath();
			logger.warn(message);
			_snrecord.setNewState(StatusNotificationLabel.FAILURE, message);
		}
		stateMachine();
	}
	
	private View loadView() throws NetworkException
	{
		_snview.setVisible(true);
		View view = new View(_viewdm);
		
		if (_recorddm == null)
		{
			view.setPortNum(_switchdm.getPortList());
		}
		else
		{
			view.setPortNum(_recorddm.getSwitchPortCount());
		}
		
		return view;
	}
	
	private void checkView()
	{
		if(_view != null)
		{
			String message = "View " + _view.getDataModel().getName() + " has been successfully created ";
			if(logger.isInfoEnabled())
				logger.info(message);
			_snview.setNewState(StatusNotificationLabel.SUCCESS, message);
		}
		else
		{
			String message = "Could not create View " + _viewdm.getName();
			logger.warn(message);
			_snview.setNewState(StatusNotificationLabel.FAILURE, message);
		}
		stateMachine();
	}
	
	private void makeHandshake() throws SwitchVizException, InterruptedException
	{
		_snconnection.setVisible(true);
		_nhs = new NetworkHandShake(_switchdm);
		_nhs.startHandShake();
	}
	
	private void checkConnectionState() throws NetworkException
	{
		if(_connectionstate == NetworkHandShake.CONNECTION)
		{
			String message = "Connection to Switch " + _switchdm.getName() + " @ " + _switchdm.getAddress() + ":" + _switchdm.getPort() + " successfull";
			if(logger.isInfoEnabled())
				logger.info(message);
			_snconnection.setNewState(StatusNotificationLabel.SUCCESS, message);
			_switchdm = _nhs.getSwitchModel();
			_switch = new Switch(_switchdm);
		}
		else if(_connectionstate == NetworkHandShake.FIRSTCONNECTION)
		{
			String message = "New Type of Switch initialized, Connection to Switch " + _switchdm.getName() + " @ " + _switchdm.getAddress() + ":" + _switchdm.getPort() + " successfull";
			if(logger.isInfoEnabled())
				logger.info(message);
			_snconnection.setNewState(StatusNotificationLabel.SUCCESS, message);
			_switchdm = _nhs.getSwitchModel();
			_switch = new Switch(_switchdm);
		}
		else if(_connectionstate == NetworkHandShake.BADCONNECTION)
		{
			String message = "Bad Connection! Packets timed out, check your Switch Configuration! Connection to Switch " + _switchdm.getName() + " @ " + _switchdm.getAddress() + ":" + _switchdm.getPort() + " established";
			if(logger.isInfoEnabled())
				logger.info(message);
			_snconnection.setNewState(StatusNotificationLabel.WARNING, message);
			_switchdm = _nhs.getSwitchModel();
			_switch = new Switch(_switchdm);
		}
		else
		{
			String message = "Could not make a Connection to Switch "  + _switchdm.getName() + " @ " + _switchdm.getAddress() + ":" + _switchdm.getPort();
			logger.warn(message);
			_snconnection.setNewState(StatusNotificationLabel.FAILURE, message);
		}
		_nhs.close();
		stateMachine();
	}
	
	private void addSignalsToReplay()
	{
		_replay = new Replay(_recorddm);
		_snreplay.setVisible(true);
		
		Iterator<ISignal> sigit = _view.getSignalList().iterator();
		
		while(sigit.hasNext())
			_replay.add(sigit.next());
	}
	
	private void openReplayFiles()
	{
		_replay.open();
	}
	
	private void checkReplay()
	{
		if (_replay != null)
		{
			String message = _replay.getSignalListSize() + " Signals has been successfully added to " + _replay.getFileListSize() + " Files ";
			if(logger.isInfoEnabled())
				logger.info(message);
			_snreplay.setNewState(StatusNotificationLabel.SUCCESS, message);
		}
		stateMachine();
		_buttstart.setEnabled(true);
	}
	
	private void addSignalsToSwitch() throws NetworkException
	{
		_snswitch.setVisible(true);
		ArrayList<ISignal> signallist = new ArrayList<ISignal>();
		
		if(_view != null)
			signallist.addAll(_view.getSignalList());
		
		if(_record != null)
			signallist.addAll(_record.getSignals());
		
		Iterator<ISignal> sigit = signallist.iterator();
		while(sigit.hasNext())
			_switch.add(sigit.next());
	}
	
	private void checkSignalsAtSwitch()
	{
		String message = _switch.getSignalListSize() + " Signals have been added to the Switch ";
		_snswitch.setNewState(StatusNotificationLabel.SUCCESS, message);
		stateMachine();
		_buttstart.setEnabled(true);
	}
	
	private synchronized void stateMachine()
	{
		if (_recorddm == null)
		{
			if(_switchdm != null && !_snconnection.isVisible())
				new ConnectionWorkerThread().execute();
			if(_viewdm != null && _switch != null && !_snview.isVisible())
				new ViewWorkerThread().execute();
			if(_switchconf != null && !_snrecord.isVisible() && _switchdm != null)
				new RecordWorkerThread().execute();
			if(_switch != null && !_snswitch.isVisible())
				if((_snrecord.isVisible() && _record != null && _viewdm == null) || 
				   (!_snrecord.isVisible() && _view != null) || 
				   (_record != null && _view != null))
				{
					new SignalSwitchWorkerThread().execute();
				}
		}
		else
		{
			if(_viewdm != null  && !_snview.isVisible())
				new ViewWorkerThread().execute();
			if(_view != null && !_snreplay.isVisible())
				new ReplayWorkerThread().execute();
		}
	}
	
	
	public void componentShown(ComponentEvent evt) 
	{
		stateMachine();
	}
	
	public void actionPerformed(ActionEvent evt) 
	{
		String command = evt.getActionCommand();
		if(ResourceManager.getInstance().getActionNew().equals(command))
		{
			Scenario zen = null;
			
			this.setVisible(false);
			if (_recorddm == null)
			{
				if(_view != null)
					_scenedm.setTitle("View " + _view.getDataModel().getName() + " at Switch " + _switch.toString());
				else
					_scenedm.setTitle("Recording at " + _switch.toString());
					
				if(_record != null)
					zen = new Scenario(_view, _switch, _scenedm, _record);
				else
					zen = new Scenario(_view, _switch, _scenedm);
			
				if(_view != null)
				{
					zen.openScene();
					new ViewLegend(_view);
				}
				else
					zen.startSNMPRequests();
			
			}
			else
			{
				_scenedm.setTitle("View " + _view.getDataModel().getName() + " with Replay " + _replay.toString());
				zen = new Scenario(_view, _scenedm, _replay);
				zen.openScene();
				zen.startReplay();
				new ViewLegend(_view);
			}
			
			
			new ScenarioStateDialog(zen);
		}
		else
		{
			this.setVisible(false);
		}
	}
	
	public void componentHidden(ComponentEvent arg0) {}
	public void componentMoved(ComponentEvent arg0) {}
	public void componentResized(ComponentEvent arg0) {}
	
	
	
	/*====================start working threads==========================*/
	
	class RecordWorkerThread extends SwingWorker<Record, Void>
	{
		public Record doInBackground() throws Exception 
		{
			if(logger.isInfoEnabled())
				logger.info("Load Record ...");
			return loadRecord();
		}
		
		public void done()
		{
			try 
			{
				_record = get();
			} 
			catch (Exception e) 
			{
				e.printStackTrace();
				logger.error(e);
				_snrecord.setNewState(StatusNotificationLabel.FAILURE, "Fatal Error occured");
			}
			checkRecord();
		}
	}
	
	class ViewWorkerThread extends SwingWorker<View, Void>
	{
		public View doInBackground() throws Exception 
		{
			if(logger.isInfoEnabled())
				logger.info("Load View ...");
			return loadView();
		}
		
		public void done()
		{
			try 
			{
				_view = get();
			} 
			catch (Exception e) 
			{
				e.printStackTrace();
				logger.error(e);
				_snview.setNewState(StatusNotificationLabel.FAILURE, "Fatal Error occured");
			}
			checkView();
		}
	}
	
	class ConnectionWorkerThread extends SwingWorker<Integer, Void>
	{
		public Integer doInBackground() throws Exception 
		{
			if(logger.isInfoEnabled())
				logger.info("Make Handshake with Switch " + _switchdm.getName());
			int parts = 4;
			long pausetime = _switchdm.getTimeout() / 4;
			if(_switchdm.getTimeout() >  2 * _scenedm.getTimeintervall())
			{
				parts = (int)((_switchdm.getTimeout() / _scenedm.getTimeintervall()) + 0.5f);
				pausetime = _scenedm.getTimeintervall();
			}
			int stat = -1;
			makeHandshake();
			for(int i = 0; i < parts; ++i)
			{
				Thread.sleep(pausetime);
				stat = _nhs.processStatus();
				if(stat == NetworkHandShake.CONNECTION)
					break; 
				if(stat == NetworkHandShake.FIRSTCONNECTION)
				{
					String message = "New Type of Switch, initialize Port List ...";
					if(logger.isInfoEnabled())
						logger.info(message);
					_snconnection.setNewState(StatusNotificationLabel.WORKING, message);
					_nhs.getPortList();
					break;
				}
			}
			return stat;
		}
		
		public void done()
		{
			try 
			{
				_connectionstate = get();
				checkConnectionState();
			} 
			catch (Exception e) 
			{
				e.printStackTrace();
				logger.error(e);
				_snconnection.setNewState(StatusNotificationLabel.FAILURE, "Fatal Error occured");
			}
		}
	}

	class SignalSwitchWorkerThread extends SwingWorker<Void, Void>
	{
		public Void doInBackground() throws Exception 
		{
			if(logger.isInfoEnabled())
				logger.info("Add Signals to Switch...");
			addSignalsToSwitch();
			return null;
		}
		
		public void done()
		{
			try 
			{
				get();
			} 
			catch (Exception e) 
			{
				e.printStackTrace();
				logger.error(e);
				_snconnection.setNewState(StatusNotificationLabel.FAILURE, "Fatal Error occured");
			}
			checkSignalsAtSwitch();
		}
	}
	
	class ReplayWorkerThread extends SwingWorker<Void, Void>
	{
		public Void doInBackground() throws Exception 
		{
			if(logger.isInfoEnabled())
				logger.info("Add Signals to Replay and Initialize Replay Files ...");
			addSignalsToReplay();
			openReplayFiles();
			return null;
		}
		
		public void done()
		{
			try 
			{
				get();
			} 
			catch (Exception e) 
			{
				e.printStackTrace();
				logger.error(e);
				_snreplay.setNewState(StatusNotificationLabel.FAILURE, "Fatal Error occured");
			}
			checkReplay();
		}
	}
}




